﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Text.RegularExpressions;
using HttpFe.Common;
using HttpFe.Managed;

namespace TalentChopper
{
    public delegate void LogHandler(string message);
    class ProxyEngine
    {
        public LogHandler Log;
        private const string DetectRequest = "CreateCourse/json/learning/saveCacheIntervalTime";
        private HttpFilteringEngine _engine;
        public int Hour { get; set; } = 0;
        public int Minute { get; set; } = 24;
        public int Second { get; set; } = 59;
        public int TotalTime { get; set; } = 71024;
        private Regex _rLearnTime;
        private Regex _rTotalTime;
        private Regex _rUrlTimestamp;
        private HttpClient _http;
        private string _lastTime = "";
        public ProxyEngine()
        {
            _engine = new HttpFilteringEngine(FirewallCheckFunc, HttpMessageBeginFunc, HttpMessageEndFunc, 80);
            _rLearnTime = new Regex(@"(?<=learnTime=)[\S\d]+?(?=&)");
            _rTotalTime = new Regex(@"(?<=studyTotalTime=)[\S\d]+?(?=&)");
            _rUrlTimestamp = new Regex(@"(?<=time=)[\S\d]+?\b");
            _http = new HttpClient(new HttpClientHandler() { UseCookies = false });
        }

        public void Start() => _engine.Start();
        public void Stop() => _engine.Stop();

        private string GetLabelContent(IEnumerable<string> content, string name)
        {
            var line = content.FirstOrDefault(s => s.StartsWith(name));
            if (string.IsNullOrEmpty(line))
            {
                return null;
            }
            return line.Remove(0, line.IndexOf(' ') + 1);
        }

        /// <summary>  
        /// DateTime时间格式转换为Unix时间戳格式  
        /// </summary>  
        /// <param name="time"> DateTime时间格式</param>  
        /// <returns>Unix时间戳格式</returns>  
        public static int ConvertDateTimeInt(System.DateTime time)
        {
            var startTime = TimeZone.CurrentTimeZone.ToLocalTime(new System.DateTime(1970, 1, 1));
            return (int)(time - startTime).TotalSeconds;
        }

        private void HttpMessageEndFunc(string requestHeaders, byte[] requestBody, string responseHeaders, byte[] responseBody, out bool shouldBlock, out byte[] customBlockResponseData)
        {
            async void Send()
            {
                var c = requestHeaders.Split(new[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries);
                var contents = c.Select(s => s.Trim());
                var origin = GetLabelContent(contents, "Origin");
                var post = GetLabelContent(contents, "POST").Replace("HTTP/1.1", "").Trim();
                var url = origin + (origin.EndsWith("/") ? "" : "/") + post;
                var time = ConvertDateTimeInt(DateTime.Now).ToString();
                url = _rUrlTimestamp.Replace(url, time);
                _lastTime = time;

                _http.DefaultRequestHeaders.Host = GetLabelContent(contents, "Host");
                _http.DefaultRequestHeaders.TryAddWithoutValidation("Cookie", GetLabelContent(contents, "Cookie"));
                _http.DefaultRequestHeaders.Referrer = new Uri(GetLabelContent(contents, "Referer"));
                _http.DefaultRequestHeaders.TryAddWithoutValidation("Origin", origin);
                _http.DefaultRequestHeaders.TryAddWithoutValidation("X-Requested-With", "XMLHttpRequestXMLHttpRequest");

                var str = Encoding.UTF8.GetString(requestBody);
                str = _rLearnTime.Replace(str, $"{Hour:D2}%3A{Minute:D2}%3A{Second:D2}");
                str = _rTotalTime.Replace(str, TotalTime.ToString());

                Dictionary<string, string> form = new Dictionary<string, string>();
                str = WebUtility.UrlDecode(str.Split(new[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries).Last());
                var pairs = str.Split(new[] { "&" }, StringSplitOptions.RemoveEmptyEntries);
                foreach (var pair in pairs)
                {
                    var p = pair.Split(new[] { "=" }, StringSplitOptions.RemoveEmptyEntries);
                    if (p.Length > 1)
                    {
                        form[p[0]] = p[1];
                    }
                    else
                    {
                        form[p[0]] = "";
                    }
                }
                var formContent = new FormUrlEncodedContent(form);
                //var hContent = new StringContent(str, Encoding.UTF8);
                var message = new HttpRequestMessage(HttpMethod.Post, url);
                message.Content = formContent;
                message.Headers.Add("Cookie", GetLabelContent(contents, "Cookie"));
                var m = await _http.SendAsync(message);
                //hContent.Headers.Add("Cookie", GetLabelContent(contents, "Cookie"));
                //var m = await _http.PostAsync(url, hContent);
                Log($"Hacking {url} ...");
                //Console.WriteLine($"Response: {m}\r\n {await m.Content.ReadAsStringAsync()}");
            }

            shouldBlock = false;
            customBlockResponseData = null;
            if (requestHeaders.Contains(DetectRequest))
            {
                shouldBlock = true;
                Send();
            }
        }

        private void HttpMessageBeginFunc(string requestHeaders, byte[] requestBody, string responseHeaders, byte[] responseBody, out ProxyNextAction nextAction, out byte[] customBlockResponseData)
        {
            nextAction = ProxyNextAction.AllowAndIgnoreContentAndResponse;
            customBlockResponseData = null;
            if (requestHeaders.Contains(DetectRequest))
            {
                if (_rUrlTimestamp.Match(requestHeaders).Value == _lastTime)
                {
                    return;
                }
                nextAction = ProxyNextAction.AllowButRequestContentInspection;
                //Console.WriteLine(requestHeaders);
            }
        }

        private bool FirewallCheckFunc(string binaryAbsolutePath)
        {
            //Who is sending this?
            return true;
        }
    }
}
